package io.gitee.thant.utils;

import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.concurrent.ConcurrentHashMap;

/*
 * 事件对象类
 */
class Event {
	public boolean flag = false; //事件标志，true表示事件已发生，初始为false
	public String name;           //事件名
	//public Object data;          //事件关联的数据，暂未使用
}

/*
 * 监听定义类
 */
class Listen {
	public Class<?> cls;    //监听类的Class
	public Object listener; //监听类的实例
	public String method;   //监听类和事件捆绑的方法 原型 void fuc(Map<String, Object>)
	public Object params;   //监听时传入的参数
}

/*
 * 事件工具类 xiaowf 2020-10-19 16:50
 * 支持事件同步，事件绑定，同步|异步事件触发
 * 极轻量，无需三方框架
 */
public class EventUtil {
	//监听事件的参数名
	public static final String listenparam = "__EventUtils.ListenParam__";   //监听参数名称，是on函数传入的
	public static final String triggerparam = "__EventUtils.TriggerParam__"; //触发参数名称，是trigger或post传入的
	
	//存储等待线程，由waitFor增加
	private static ConcurrentHashMap<String, List<Thread>> _binds = new ConcurrentHashMap<String, List<Thread>>();
	//存储监听事件和响应方法，由on/off函数维护
	private static ConcurrentHashMap<String, List<Listen>> _listens = new ConcurrentHashMap<String, List<Listen>>();
	//存储事件对象
	private static ConcurrentHashMap<String, Event> _events = new ConcurrentHashMap<String, Event>();
	
	//存储异步触发事件时的事件名和参数，只用于post函数
	private static ConcurrentHashMap<String, Object> _posts = new ConcurrentHashMap<String, Object>();
	private static Thread _worker; //post函数用到的异步后台线程

	/*
	 * 绑定等待事件
	 */
	private static void bindWait(String evtname, Thread thd) {
		List<Thread> lst = _binds.get(evtname);
		if (null ==  lst) {
			lst = new ArrayList<Thread>(1);
			_binds.put(evtname, lst);
		}
		lst.add(thd);
	}
	
	/*
	 * 绑定监听事件
	 */
	private static void bindListen(String evtname, Listen lsn) {
		List<Listen> lst = _listens.get(evtname);
		if (null ==  lst) {
			lst = new ArrayList<Listen>(1);
			_listens.put(evtname, lst);
		}
		lst.add(lsn);
	}

	/*
	 * API:等待事件完成
	 */
	public static boolean waitFor(String evtname, long timeout) {
		long t = System.currentTimeMillis();
		Thread thd = Thread.currentThread();
		Event evt = getEvent(evtname);

		bindWait(evtname, thd);
		synchronized (thd) {
			while (!evt.flag) {
				if (timeout > 0 && System.currentTimeMillis() - t >= timeout) {
					return false;
				}
				try {
					thd.wait(timeout);
				} catch (InterruptedException e) {
					e.printStackTrace();
					return false;
				}
			}
		}
		return true;
	}
	
	/*
	 * API:监听事件，事件发生时调用绑定的对象方法
	 */
	public static void on(String evtname, Object ins, String method, Object params) {
		off(evtname, ins);
		
		Listen ls = new Listen();
		if (ins instanceof Class) {
			ls.cls = (Class<?>)ins;
			ls.listener = null;
		} else {
			ls.cls = ins.getClass();
			ls.listener = ins;
		}
		ls.method = method;
		ls.params = params;
		bindListen(evtname, ls);
	}
	
	/*
	 * API:删除事件监听，on的反处理
	 */
	public static void off(String evtname, Object ins) {
		List<Listen> lst = _listens.get(evtname);
		if (null == lst) return;
		
		if (null == ins) {
			_listens.remove(evtname);
		} else {
			Listen ls;
			for (int i=0; i<lst.size(); ++i) {
				ls = lst.get(i);
				if (ls.cls == ins || ls.listener == ins) {
					lst.remove(i);
					break;
				}
			}
			
			if (lst.size()<=0) {
				_listens.remove(evtname);
			}
		}
	}

	//获取事件对象，singleton，用字符串唯一标识一个事件对象
	private static Event getEvent(String evtname) {
		Event evt = _events.get(evtname);
		if (null == evt) {
			evt = new Event();
			evt.name = evtname;
			_events.put(evtname, evt);
		}
		return evt;
	}

	/*
	 * 通知事件发生，让调用waitFor的代码继续执行
	 */
	private static void notify(String evtname) {
		List<Thread> lst = _binds.get(evtname);
		if (lst != null) {
			for (int i=0; i<lst.size(); ++i) {
				Thread thd = lst.get(i);
				synchronized (thd) {
					thd.notifyAll();
				}
			}
			_binds.remove(evtname);
		}
	}
	
	/*
	 * 处理监听事件，调用事件绑定的对象方法
	 */
	private static void handle(String evtname, Object params) {
		List<Listen> lst = _listens.get(evtname);
		if (lst != null) {
			Map<String, Object> args = new HashMap<String, Object>();
			args.put(triggerparam, params);
			for (int i=0; i<lst.size(); ++i) {
				Listen ls = lst.get(i);
				try {
					Method mtd = ls.cls.getMethod(ls.method, Map.class);
					args.put(listenparam, ls.params);
					mtd.invoke(ls.listener, args);
				} catch (Exception e) {
					e.printStackTrace();
				}
			}
		}
	}

	/*
	 * API:同步触发事件，触发事件后不会自动清除事件标志，要清除需手工调用resetEvent
	 */
	public static void trigger(String evtname, Object params) {
		getEvent(evtname).flag = true;

		notify(evtname);
		handle(evtname, params);
	}

	/*
	 * API:异步触发事件
	 */
	public static void post(String evtname, Object params) {
		_posts.put(evtname, params);

		if (null == _worker) {
			_worker = new Thread() {
				@Override
				public void run() {
					synchronized(_posts) {
						while(!Thread.interrupted()) {
							for (Entry<String, Object> item : _posts.entrySet()) {
								EventUtil.trigger(item.getKey(), item.getValue());
							}
							_posts.clear();
							try {
								_posts.wait();
							} catch (InterruptedException e) {
								return;
							}
						}
					}
				}
			};
			_worker.start();
		} else {
			synchronized(_posts) {
				_posts.notify();
			}
		}
	}

	/*
	 * API:清除事件标志
	 */
	public static void resetEvent(String evtname) {
		getEvent(evtname).flag = false;
	}

	/*
	 * API:关闭，不调用的话可能导致后台工作线程无法退出
	 */
	public static void close() {
		if (_worker != null) {
			_worker.interrupt();
			_worker = null;
		}
	}
}
